package com.tql.mall.gateway.filter;


import com.alibaba.fastjson.JSONObject;
import com.tql.mall.common.constant.ComConstant;
import com.tql.mall.common.result.R;
import com.tql.mall.common.result.RC;
import com.tql.mall.common.util.IpUtil;
import com.tql.mall.gateway.constant.GateConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * @Author: tyk
 * @Date: 2023/5/18 10:06
 * @Description: 鉴权
 */
@Component
public class AuthGlobalFilter implements GlobalFilter {

    @Autowired
    private RedisTemplate redisTemplate;

    // 匹配路径的工具类
    private final AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Value("${authUrls.url}")
    private String authUrls;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 请求对象
        ServerHttpRequest req = exchange.getRequest();
        // 资源路径
        String path = req.getURI().getPath();

        // 内部接口不允许访问
        if (antPathMatcher.match(GateConstant.INNER_PATTERN, path))
            return out(exchange.getResponse(), RC.PERMISSION);

        // 用户Id
        String userId = getUserId(req);
        // 临时用户ID
        String userTempId = getUserTempId(req);

        // IP异常
        if (GateConstant.IP_ERROR_FLAG.equals(userId))
            return out(exchange.getResponse(), RC.IP_ERROR);


        // 需认证的接口（未登录不可访问）
        if (antPathMatcher.match(GateConstant.AUTH_PATTERN, path)) {
            if (StringUtils.isEmpty(userId))
                return out(exchange.getResponse(), RC.LOGIN_AUTH);
        }

        // 白名单（需登录才能访问）
        for (String url : authUrls.split(GateConstant.URL_SEP)) {
            if (path.contains(url) && StringUtils.isEmpty(userId)) {
                ServerHttpResponse resp = exchange.getResponse();
                // 303重定向
                resp.setStatusCode(HttpStatus.SEE_OTHER);
                resp.getHeaders().set(HttpHeaders.LOCATION, ComConstant.LOGIN_PAGE + req.getURI());
                // 重定向到登录
                return resp.setComplete();
            }
        }


        // 设置用户ID到请求头
        if (!StringUtils.isEmpty(userId) || !StringUtils.isEmpty(userTempId)) {
            if (!StringUtils.isEmpty(userId))
                req.mutate().header(ComConstant.USER_ID, userId).build();
            if (!StringUtils.isEmpty(userTempId))
                req.mutate().header(ComConstant.USER_TEMP_ID, userTempId).build();
            // 将现在的request 变成 exchange对象
            return chain.filter(exchange.mutate().request(req).build());
        }

        // 放行
        return chain.filter(exchange);
    }

    /**
     * 获取用户临时ID
     */
    private String getUserTempId(ServerHttpRequest req) {
        String userTempId = "";

        // 从请求头获取
        List<String> list = req.getHeaders().get(ComConstant.USER_TEMP_ID);
        if (!CollectionUtils.isEmpty(list)) userTempId = list.get(0);

        // 从cookie中取
        HttpCookie cookie = req.getCookies().getFirst(ComConstant.USER_TEMP_ID);
        if (cookie != null) userTempId = cookie.getValue();

        return userTempId;
    }

    /**
     * 获取当前登录用户id
     */
    private String getUserId(ServerHttpRequest req) {
        String token = "";

        // 从请求头/cookie获取token
        List<String> tokens = req.getHeaders().get(ComConstant.TOKEN);
        if (!CollectionUtils.isEmpty(tokens))
            token = tokens.get(0);
            // 从cookie中获取token
        else {
            MultiValueMap<String, HttpCookie> cookies = req.getCookies();
            HttpCookie cookie = cookies.getFirst(ComConstant.TOKEN);
            if (cookie != null)
                token = URLDecoder.decode(cookie.getValue());
        }

        // 获取到token
        if (!StringUtils.isEmpty(token)) {

            // 从redis中获取用户信息
            String userStr = (String) redisTemplate.opsForValue().get(GateConstant.USER_LOGIN_TOKEN_PREFIX + token);
            if (StringUtils.isEmpty(userStr)) return "";
            JSONObject userJson = JSONObject.parseObject(userStr);

            // 校验IP
            String ip = userJson.getString(ComConstant.IP);
            String curIp = IpUtil.getGatewayIpAddress(req);
            if (!ip.equals(curIp)) return GateConstant.IP_ERROR_FLAG;

            return userJson.getString(ComConstant.USER_ID);
        }
        return "";
    }


    /**
     * 鉴权失败的后续处理
     */
    private Mono<Void> out(ServerHttpResponse resp, RC rc) {
        // 返回鉴权失败的响应结果
        byte[] bits = JSONObject.toJSONString(R.fail(rc)).getBytes(StandardCharsets.UTF_8);
        DataBuffer wrap = resp.bufferFactory().wrap(bits);
        // 中文乱码处理
        resp.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        // 输出到页面
        return resp.writeWith(Mono.just(wrap));
    }

}